import java.util.*;

class Motion
{
    private String boneName;
    private ArrayList<double[]> motion;
    private Pyramid[] pyramids;
    private double[][] limits;
    private int axesCount = 0;
    private int axC = 0;

    public Motion(String n)
    {
        this.boneName = n;
        this.motion = new ArrayList<double[]>();
        this.pyramids = null;
        axC = 0;
    }

    //Shallow copy of motion - no wave data copied
    public Motion(Motion a)
    {
        this.boneName = a.name();
        this.motion = new ArrayList<double[]>();
        this.pyramids = null;
        this.limits = a.limits;
        axC = 0;
    }

    public void trim(int i)
    {
        ArrayList<double[]> temp = new ArrayList<double[]>();
        for (int j = 0; j < i; j++)
            temp.add(motion.get(j));
        motion = temp;
    }

    public void clearMotions()
    {
        this.motion = new ArrayList<double[]>();
    }

    public void setLimits(double[][] l)
    {
        limits = l;
    }

    public double[][] getLimits()
    {
        return limits;
    }

    public void addLaplacian(int i, double[][] x)
    {
        for (int j = 0; j < this.laplacianSize(); j++)
        {
            this.pyramids[i].addLaplacian(j, x[j]);
        }
    }
    
    public double[][] getLaplacian(int i)
    {
        return pyramids[i].getLaplacian();
    }
    
    public void updateLimits()
    {
        if (!boneName.equals("root"))
        {
            for (int i = 0; i < motion.size(); i++)
            {
                for (int j = 0; j < motion.get(i).length; j++)
                {
                    if (limits[j][0] > motion.get(i)[j])
                        motion.get(i)[j] = limits[j][0];
                    if (limits[j][1] < motion.get(i)[j])
                        motion.get(i)[j] = limits[j][1];
                }
            }
        }
    }

    public void reconstruct()
    {
        for (int i = 0; i < pyramids.length; i++)
        {
            double[] wave = pyramids[i].reconstruct();
            for (int j = 0; j < wave.length; j++)
                motion.get(j)[i] = wave[j];
        }
    }

    public int numWaves()
    {
        return this.motion.get(0).length;
    }

    public int laplacianSize()
    {
        return this.pyramids[0].laplacianSize();
    }

    public void multiplyLaplacian(int i, int j, double x)
    {
        this.pyramids[i].multiplyLaplacian(j, x);
    }

    public void createPyramids()
    {
        pyramids = new Pyramid[motion.get(0).length];
        for (int i = 0; i < motion.get(0).length; i++)
        {
            double[] wave = getWave(i);
            pyramids[i] = new Pyramid(wave);
        }
    }

    public double[] getFrame(int i)
    {
        return this.motion.get(i);
    }

    public double[] getWave(int i)
    {
        double[] wave = new double[motion.size()];
        for (int j = 0; j < motion.size(); j++)
        {
            wave[j] = motion.get(j)[i];
        }
        return wave;
    }

    public void warp(Motion m)
    {
        TimeWarp t = new TimeWarp(m.getWave(0), this.getWave(0));
    }

    public String name()
    {
        return this.boneName;
    }

    public void setName(String n)
    {
        this.boneName = n;
    }

    public void addMotion(double[] m)
    {
        this.motion.add(m);
    }

    public void initWaves(int axes, int count)
    {
        for (int i = 0; i < count; i++)
        {
            this.motion.add(new double[axes]);
        }
    }

    public void addWave(int axis,double[] wave)
    {
        for (int i = 0; i < wave.length; i++)
        {
            this.motion.get(i)[axis] = wave[i];
        }
    }

    public void print()
    {
        System.out.println(this.boneName);
        for (int i = 0; i < this.motion.size(); i++)
        {
            for (int j = 0; j < this.motion.get(i).length; j++)
                System.out.print(this.motion.get(i)[j] + " ");
            System.out.println("");
        }
    }

    public int size()
    {
        return this.motion.size();
    }

}
